package com.huiyun.project.pay.biz.controller.pay.wxpay;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.kit.*;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.WxPayApiConfig;
import com.ijpay.wxpay.WxPayApiConfigKit;
import com.ijpay.wxpay.model.GetTransferInfoModel;
import com.ijpay.wxpay.model.RefundQueryModel;
import com.ijpay.wxpay.model.TransferModel;
import com.huiyun.project.common.core.domain.R;
import com.huiyun.project.common.core.util.fn.FunctionUtils;
import com.huiyun.project.common.security.annotation.Inner;
import com.huiyun.project.pay.biz.controller.BaseController;
import com.huiyun.project.pay.biz.dto.PayOrderDto;
import com.huiyun.project.pay.biz.entity.PayChannel;
import com.huiyun.project.pay.biz.handler.callback.impl.WxPayCallbackHandler;
import com.huiyun.project.pay.biz.handler.pay.WxPayHandler;
import com.huiyun.project.pay.biz.kit.PayConfigKit;
import com.huiyun.project.pay.biz.mq.NotifyProducer;
import com.huiyun.project.pay.client.consts.MchNotifyConst;
import com.huiyun.project.pay.client.consts.PayConst;
import com.huiyun.project.pay.client.vo.NotifyResp;
import com.huiyun.project.pay.client.vo.PayReq;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Javen
 */
@Controller
@RequestMapping("/wxPay")
@Api(tags = "微信支付")
@Slf4j
public class WxPayController extends BaseController {
    @Autowired
    private WxPayHandler wxPayHandler;
    @Autowired
    private WxPayCallbackHandler wxPayCallbackHandler;
    @Autowired
    private NotifyProducer notifyProducer;

    /**
     * 统一接口下单
     *
     * @param params
     * @return
     */
    @Inner(value = false)
    @PostMapping(value = "/payOrder/create")
    @ResponseBody
    @ApiOperation("微信统一下单接口,生产支付预订单")
    public R<String> payOrder(@RequestBody PayReq params) {
        return FunctionUtils.wrapper(() -> {
            log.debug("-------------[商户统一下单]开始接收统一下单请求 {}-------------", params.getChannelAppId());
            PayConfigKit.init(params.getChannelAppId());
            PayOrderDto payOrderDto = createPayOrder(params);
            String channelId = payOrderDto.getChannelId();
            if (PayConst.PAY_CHANNEL_WX_MINI.equals(channelId)) {
                return wxPayHandler.miniAppPay(payOrderDto);
            } else if (PayConst.PAY_CHANNEL_WX_APP.equals(channelId)) {
                return wxPayHandler.appPay(payOrderDto);
            } else if (PayConst.PAY_CHANNEL_WX_JSAPI.equals(channelId)) {
                return wxPayHandler.mpPay(payOrderDto);
            } else {
                return R.failed("不支持的微信支付渠道类型[channelId=" + channelId + "]");
            }
        });
    }


    /**
     * 扫码模式一
     */
    @RequestMapping(value = "/scanCode1", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    @ApiOperation("扫码模式一")
    public R scanCode1(HttpServletRequest request, HttpServletResponse response,
                       @RequestParam("productId") String productId) {
        try {
            if (StrUtil.isBlank(productId)) {
                return R.failed("productId is null");
            }
            WxPayApiConfig config = WxPayApiConfigKit.getWxPayApiConfig();
            //获取扫码支付（模式一）url
            String qrCodeUrl = WxPayKit.bizPayUrl(config.getPartnerKey(), config.getAppId(), config.getMchId(), productId);
            log.info(qrCodeUrl);
            //生成二维码保存的路径
            String name = "payQRCode1.png";
            log.info(ResourceUtils.getURL("classpath:").getPath());
            Boolean encode = QrCodeKit.encode(qrCodeUrl, BarcodeFormat.QR_CODE, 3, ErrorCorrectionLevel.H,
                    "png", 200, 200,
                    ResourceUtils.getURL("classpath:").getPath().concat("static").concat(File.separator).concat(name));
            if (encode) {
                //在页面上显示
                return R.ok(name);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return R.failed("系统异常：" + e.getMessage());
        }
        return null;
    }


    /**
     * 企业付款到零钱
     */
    @RequestMapping(value = "/transfer", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    @ApiOperation("企业付款到零钱")
    public String transfer(HttpServletRequest request, @RequestParam("openId") String openId) {

        String ip = IpKit.getRealIp(request);
        if (StrUtil.isBlank(ip)) {
            ip = "127.0.0.1";
        }

        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

        Map<String, String> params = TransferModel.builder()
                .mch_appid(wxPayApiConfig.getAppId())
                .mchid(wxPayApiConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .partner_trade_no(WxPayKit.generateStr())
                .openid(openId)
                .check_name("NO_CHECK")
                .amount("100")
                .desc("IJPay 让支付触手可及-企业付款")
                .spbill_create_ip(ip)
                .build()
                .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);

        // 提现
        String transfers = WxPayApi.transfers(params, wxPayApiConfig.getCertPath(), wxPayApiConfig.getMchId());
        log.info("提现结果:" + transfers);
        Map<String, String> map = WxPayKit.xmlToMap(transfers);
        String returnCode = map.get("return_code");
        String resultCode = map.get("result_code");
        if (WxPayKit.codeIsOk(returnCode) && WxPayKit.codeIsOk(resultCode)) {
            // 提现成功
        } else {
            // 提现失败
        }
        return transfers;
    }

    /**
     * 查询企业付款到零钱
     */
    @RequestMapping(value = "/transferInfo", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    @ApiOperation("查询企业付款到零钱")
    public String transferInfo(@RequestParam("partner_trade_no") String partnerTradeNo) {
        try {
            WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

            Map<String, String> params = GetTransferInfoModel.builder()
                    .nonce_str(WxPayKit.generateStr())
                    .partner_trade_no(partnerTradeNo)
                    .mch_id(wxPayApiConfig.getMchId())
                    .appid(wxPayApiConfig.getAppId())
                    .build()
                    .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);

            return WxPayApi.getTransferInfo(params, wxPayApiConfig.getCertPath(), wxPayApiConfig.getMchId());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取RSA加密公钥
     */
    @RequestMapping(value = "/getPublicKey", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    @ApiOperation("获取RSA加密公钥")
    public String getPublicKey() {
        try {
            WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

            Map<String, String> params = new HashMap<String, String>(4);
            params.put("mch_id", wxPayApiConfig.getMchId());
            params.put("nonce_str", String.valueOf(System.currentTimeMillis()));
            params.put("sign_type", "MD5");
            String createSign = WxPayKit.createSign(params, wxPayApiConfig.getPartnerKey(), SignType.MD5);
            params.put("sign", createSign);
            return WxPayApi.getPublicKey(params, wxPayApiConfig.getCertPath(), wxPayApiConfig.getMchId());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 企业付款到银行卡
     */
    @RequestMapping(value = "/payBank", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    @ApiOperation("企业付款到银行卡")
    public String payBank() {
        try {
            WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

            //通过WxPayApi.getPublicKey接口获取RSA加密公钥
            //假设获取到的RSA加密公钥为PUBLIC_KEY(PKCS#8格式)
            final String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6Bl76IwSvBTiibZ+CNRUA6BfahMshZ0WJpHD1GpmvcQjeN6Yrv6c9eIl6gB4nU3isN7bn+LmoVTpH1gHViaV2YyG/zXj4z4h7r+V+ezesMqqorEg38BCNUHNmhnw4/C0I4gBAQ4x0SJOGnfKGZKR9yzvbkJtvEn732JcEZCbdTZmaxkwlenXvM+mStcJaxBCB/h5xJ5VOF5nDbTPzLphIpzddr3zx/Jxjna9QB1v/YSKYXn+iuwruNUXGCvvxBWaBGKrjOdRTRy9adWOgNmtuYDQJ2YOfG8PtPe06ELKjmr2CfaAGrKKUroyaGvy3qxAV0PlT+UQ4ADSXWt/zl0o5wIDAQAB";

            Map<String, String> params = new HashMap<String, String>(10);
            params.put("mch_id", wxPayApiConfig.getMchId());
            params.put("partner_trade_no", System.currentTimeMillis() + "");
            params.put("nonce_str", System.currentTimeMillis() + "");
            //收款方银行卡号
            params.put("enc_bank_no", RsaKit.encryptByPublicKeyByWx("银行卡号", PUBLIC_KEY));
            //收款方用户名
            params.put("enc_true_name", RsaKit.encryptByPublicKeyByWx("银行卡持有人姓名", PUBLIC_KEY));
            //收款方开户行
            params.put("bank_code", "1001");
            params.put("amount", "1");
            params.put("desc", "IJPay 让支付触手可及-付款到银行卡");
            params.put("sign", WxPayKit.createSign(params, wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256));
            return WxPayApi.payBank(params, wxPayApiConfig.getCertPath(), wxPayApiConfig.getMchId());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 查询企业付款到银行
     */
    @RequestMapping(value = "/queryBank", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    @ApiOperation("查询企业付款到银行")
    public String queryBank(@RequestParam("partner_trade_no") String partnerTradeNo) {
        try {
            WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

            Map<String, String> params = new HashMap<String, String>(4);
            params.put("mch_id", wxPayApiConfig.getMchId());
            params.put("partner_trade_no", partnerTradeNo);
            params.put("nonce_str", System.currentTimeMillis() + "");
            params.put("sign", WxPayKit.createSign(params, wxPayApiConfig.getPartnerKey(), SignType.MD5));
            return WxPayApi.queryBank(params, wxPayApiConfig.getCertPath(), wxPayApiConfig.getMchId());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 微信退款查询
     */
    @RequestMapping(value = "/refundQuery", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    @ApiOperation("微信退款查询")
    public String refundQuery(@RequestParam("transactionId") String transactionId,
                              @RequestParam("out_trade_no") String outTradeNo,
                              @RequestParam("out_refund_no") String outRefundNo,
                              @RequestParam("refund_id") String refundId) {

        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

        Map<String, String> params = RefundQueryModel.builder()
                .appid(wxPayApiConfig.getAppId())
                .mch_id(wxPayApiConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .transaction_id(transactionId)
                .out_trade_no(outTradeNo)
                .out_refund_no(outRefundNo)
                .refund_id(refundId)
                .build()
                .createSign(wxPayApiConfig.getPartnerKey(), SignType.MD5);

        return WxPayApi.orderRefundQuery(false, params);
    }

    /**
     * 退款通知
     * <xml><return_code>SUCCESS</return_code><appid><![CDATA[wxd37f1a2c7af9795c]]></appid><mch_id><![CDATA[1564493021]]></mch_id><nonce_str><![CDATA[44f71650ecf5227310eb6e4f886cec7a]]></nonce_str><req_info><![CDATA[G6CvFZOAYdXBxYq5l9joCqwZT4mIjW+eT3pyLd4VC4Gl4biB2YXAR1hpZN+knBZ8p+N4Nlg13Rr/zuHwDgnKqEy5KcKd3nKM4SbRfLS6Va3wJPwiS156oCaNAf7wLUZ45pHleNnKihk2O49NeEtWf6FzGG9y6CwcmgOqUzIWiVnG7NK5LWl7Ry3mu+ZJQW1Re8cyrGUyV2q0wgiNIoaZKymuwnMUSCDL1NzDVDn/iNBoUEWoG5UjEVKY9mArM33xR8I1uQHOHZdBfBdSwHvSunzD5zS1G1cTk0sesel4k5Djx8o34OSMT/U8cJd59bzzdNYRxyTeFUuYLmIRClFZLbrEjO1Tu1slE1SRSNqxnBDwhWsMDYjjHXpRPAgztSuywdvQooibagX9EC+zLc2CD1ocEHYxuNxcjwSyAM4JtTczljSGFe8O+DPtXsh0iS0AXqTscWdIpEdD+GI76dUFz5pn+u68lJx1akgljjFgjrsogpRQEeVD75wyfDbI/LCUhAm55gYAjMp9Ta9T/bWZttNQL8o2iUHd6WKvWYgHFX2zeBP5cWTjK8ssui8GJRLQS+/P7X7ciMPAiD5TzADOy4/GzmlagRbjAMsuo7pSx+n5T9KGP0jThAjDpfhHkQPRgkkW9N6aB9LAbOucDZK0ZR2PrTwHtkc/CdxHE6pzGngANq6ipuLQqEVRnIJ0hCgUyAjKMXSqcdfUPkpzMDGTv8Ty1GRBNBw27/TL36qSS1uvwA/y8kcvv/5InHoD8c3Wm6muhkmhohoUgJEDdN3MTruu43pai/vf0JRYArcLOSE5dKUYUbloFj4ZO9/0FUm25T6RGFHMXgHxIxh33IgyMCgCyBGtIPjyKp/AGU6B0kgMHrlj0ux1N0zygIwAbpcUpNZzK/hBkfaBkGAviroL5FwKUAKDr05wXqHD5e8xvNtgjpyRr9MLmOCBS3ZTcQ+rJ3hT92VwGVQV2SCFhWAEU7GNsXPGVFr7I+qJmInvj+y0VNblP+wbT3EPw6jJPZBkZ5oWFUhVXu9RRloXpWasSyMnB0NJMHBsVS4Kq5X3eZU=]]></req_info></xml>
     */
    @RequestMapping(value = "/refundNotify/{channelAppId}", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    @Inner(value = false)
    @ApiOperation("微信退款通知")
    public String refundNotify(@PathVariable String channelAppId, HttpServletRequest request) {
        try {
            PayConfigKit.init(channelAppId);
            String xmlMsg = HttpKit.readData(request);
            log.info("退款通知=" + xmlMsg);
            PayChannel payChannel = payChannelService.getByChannelAppId(channelAppId);
            Map<String, String> params = PayKit.xmlToMap(xmlMsg);
            String returnCode = params.get("return_code");
            // 注意重复通知的情况，同一订单号可能收到多次通知，请注意一定先判断订单状态
            //if (WxPayKit.codeIsOk(returnCode)) {
            String reqInfo = params.get("req_info");
            String decryptData = WxPayKit.decryptData(reqInfo, WxPayApiConfigKit.getWxPayApiConfig().getPartnerKey());
            log.info("退款通知解密后的数据=" + decryptData);
            Map<String, String> paramsData = PayKit.xmlToMap(decryptData);
            String code = WxPayKit.codeIsOk(returnCode) && WxPayKit.codeIsOk(paramsData.get("refund_status")) ? "SUCCESS" : "FAIL";

            String mchOrderNo = params.get(PayConst.OUT_TRADE_NO);
            //发送支付事务MQ消息
            notifyProducer.sendMessage(new NotifyResp()
                    .setCode(code)
                    .setMessage("")
                    .setDestination(PayConst.TOPIC_MALL_REFUND_NOTIFY)
                    .setMchId(payChannel.getMchId())
                    .setType(MchNotifyConst.TYPE.REFUND.getValue())
                    .setChannelId(payChannel.getChannelId())
                    .setMchOrderNo(mchOrderNo)
                    .setTradeNo(params.get("transaction_id"))
                    .setTotalAmount(Convert.toLong(params.get("total_fee")))
                    .setChannelAppId(channelAppId)
                    .setParams(params)
            );
            //4.返回通知结果
            Map<String, String> xml = new HashMap(2);
            xml.put("return_code", "SUCCESS");
            xml.put("return_msg", "OK");
            return WxPayKit.toXml(xml);
        } catch (Exception ex) {
            log.error(ExceptionUtils.getMessage(ex));
            return null;
        }
    }


    /**
     * 异步通知
     */
    @RequestMapping(value = "/payNotify/{channelAppId}", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    @ApiOperation("微信支付通知")
    @Inner(value = false)
    public String payNotify(@PathVariable String channelAppId, HttpServletRequest request) {
        try {
            PayConfigKit.init(channelAppId);
            log.info("微信支付结果通知开始channelAppId:{}", channelAppId);
            //1.验证并解析参数
            String xmlMsg = HttpKit.readData(request);
            log.info("支付通知信息:" + xmlMsg);
            PayChannel payChannel = payChannelService.getByChannelAppId(channelAppId);
            Map<String, String> params = wxPayCallbackHandler.parseAndVerify(channelAppId, payChannel.getChannelId(), xmlMsg);
            String mchOrderNo = params.get(PayConst.OUT_TRADE_NO);
            //发送支付事务MQ消息
            notifyProducer.sendMessage(new NotifyResp()
                    .setCode(params.get("result_code"))
                    .setMessage(params.get("err_code_des"))
                    .setDestination(PayConst.TOPIC_MALL_PAY_NOTIFY)
                    .setMchId(payChannel.getMchId())
                    .setType(MchNotifyConst.TYPE.PAY.getValue())
                    .setChannelId(payChannel.getChannelId())
                    .setMchOrderNo(mchOrderNo)
                    .setTradeNo(params.get("transaction_id"))
                    .setTotalAmount(Convert.toLong(params.get("total_fee")))
                    .setChannelAppId(channelAppId)
                    .setParams(params)
            );
            //4.返回通知结果
            Map<String, String> xml = new HashMap(2);
            xml.put("return_code", "SUCCESS");
            xml.put("return_msg", "OK");
            return WxPayKit.toXml(xml);
        } catch (Exception ex) {
            log.error(ExceptionUtils.getMessage(ex));
            return null;
        }
    }
}
